@file:JvmMultifileClass
@file:JvmName("MapUtils")

package hos.kotlin

/**
 * <p>Title: IterableExt </p>
 * <p>Description:  </p>
 * <p>Company: www.mapuni.com </p>
 *
 * @author : 蔡俊峰
 * @date : 2024-07-12 11:42
 * @version : 1.0
 */
public inline fun <K, T> Map<K, T>?.count(action: K?.(value: T) -> Boolean): Int {
    if (isNullOrEmpty()) {
        return 0
    }
    var count = 0
    forEach { (k, t) ->
        if (action.invoke(k, t)) {
            count++
        }
    }
    return count
}

fun <K, T> Map<K, T>?.includes(item: T?): Boolean {
    return this?.containsValue(item) ?: false
}

fun <K, T> Map<K, T>?.isNull(key: K): Boolean {
    if (isNullOrEmpty() || !containsKey(key)) {
        return true;
    }
    try {
        val element = this[key]
        return element == null ||
                "null" == element ||
                "NULL" == element ||
                "Null" == element
    } catch (_: Throwable) {
    }
    return true
}

@JvmOverloads
fun <K, T> Map<K, T>?.getValue(
    key: K,
    defaultValue: T? = null,
    convert: (K.(value: T) -> T)? = null
): T? {
    if (this == null || isNull(key)) {
        return defaultValue
    }

    return try {
        val value = this[key] ?: return defaultValue
        return convert?.invoke(key, value) ?: value
    } catch (_: Throwable) {
        defaultValue
    }
}

@JvmOverloads
fun <K, T> Map<K, T>?.get(
    key: K, defaultValue: T,
    convert: (K.(value: T) -> T)? = null
): T {
    return getValue(key, defaultValue, convert) ?: defaultValue
}

@JvmOverloads
fun <K, T, E> Map<K, T>?.getList(
    key: K,
    defaultValue: MutableList<E>? = null
): MutableList<E>? {
    val value = getValue(key, defaultValue) ?: return defaultValue
    if (value is MutableList<*>) {
        @Suppress("UNCHECKED_CAST")
        return value.map {
            when (it) {
                is Int -> {
                    return@map it.toInt() as E
                }

                is Double -> {
                    return@map it.toDouble() as E
                }

                is Float -> {
                    return@map it.toFloat() as E
                }

                is Long -> {
                    return@map it.toLong() as E
                }

                is Short -> {
                    return@map it.toShort() as E
                }

                is Boolean -> {
                    return@map it as E
                }

                is String -> {
                    return@map it.toString() as E
                }

                else -> return@map it as E
            }
        }.toMutableList()
    }
    return defaultValue
}

@JvmOverloads
fun <K, T> Map<K, T>?.getMap(key: K, defaultValue: Map<*, *>? = null): Map<*, *>? {
    val value = getValue(key, defaultValue) ?: return defaultValue
    if (value is Map<*, *>) {
        return value
    }
    return defaultValue
}

@JvmOverloads
fun <K, T> Map<K, T>?.getString(
    key: K,
    defaultValue: String? = null,
    convert: (K.(value: T) -> String)? = null
): String? {
    val value = getValue(key) ?: return defaultValue
    if (value is String) {
        return value
    }
    return convert?.invoke(key, value) ?: value.toString()
}

@JvmOverloads
fun <K, T> Map<K, T>?.optString(
    key: K,
    defaultValue: String = "",
    convert: (K.(value: T) -> String)? = null
): String {
    return getString(key, defaultValue, convert) ?: defaultValue
}

@JvmOverloads
fun <K, T> Map<K, T>?.getInt(
    key: K,
    defaultValue: Int? = null,
    convert: (K.(value: T) -> Int)? = null
): Int? {
    val value = getValue(key) ?: return defaultValue
    if (value is Int) {
        return value
    }
    return convert?.invoke(key, value) ?: value.toString().toIntOrNull()
}

@JvmOverloads
fun <K, T> Map<K, T>?.optInt(
    key: K,
    defaultValue: Int = 0,
    convert: (K.(value: T) -> Int)? = null
): Int {
    return getInt(key, defaultValue, convert) ?: defaultValue
}

@JvmOverloads
fun <K, T> Map<K, T>?.getDouble(
    key: K,
    defaultValue: Double? = null,
    convert: (K.(value: T) -> Double)? = null
): Double? {
    val value = getValue(key) ?: return defaultValue
    if (value is Double) {
        return value
    }
    return convert?.invoke(key, value) ?: value.toString().toDoubleOrNull()
}

@JvmOverloads
fun <K, T> Map<K, T>?.optDouble(
    key: K,
    defaultValue: Double = 0.0,
    convert: (K.(value: T) -> Double)? = null
): Double {
    return getDouble(key, defaultValue, convert) ?: defaultValue
}

@JvmOverloads
fun <K, T> Map<K, T>?.getFloat(
    key: K,
    defaultValue: Float? = null,
    convert: (K.(value: T) -> Float)? = null
): Float? {
    val value = getValue(key) ?: return defaultValue
    if (value is Float) {
        return value
    }
    return convert?.invoke(key, value) ?: value.toString().toFloatOrNull()
}

@JvmOverloads
fun <K, T> Map<K, T>?.optFloat(
    key: K,
    defaultValue: Float = 0.0f,
    convert: (K.(value: T) -> Float)? = null
): Float {
    return getFloat(key, defaultValue, convert) ?: defaultValue
}

@JvmOverloads
fun <K, T> Map<K, T>?.getBoolean(
    key: K,
    defaultValue: Boolean? = null,
    convert: (K.(value: T) -> Boolean)? = null
): Boolean? {
    val value = getValue(key) ?: return defaultValue
    if (value is Boolean) {
        return value
    }
    return convert?.invoke(key, value) ?: value.toString().toBooleanStrictOrNull()
}

@JvmOverloads
fun <K, T> Map<K, T>?.optBoolean(
    key: K,
    defaultValue: Boolean = false,
    convert: (K.(value: T) -> Boolean)? = null
): Boolean {
    return getBoolean(key, defaultValue, convert) ?: defaultValue
}

fun <K, T> Map<K, T>?.forEach(block: K.(T?) -> Unit) {
    if (this == null) {
        return
    }
    keys.forEach {
        block.invoke(it, this[it])
    }
}

fun <K, T> Map<K, T>?.forEachValue(block: T?.() -> Unit) {
    if (this == null) {
        return
    }
    keys.forEach {
        block.invoke(this[it])
    }
}

public inline fun <K, T> Map<K, T>?.forEachCanBreak(action: K.(value: T?) -> Boolean) {
    if (this == null) {
        return
    }
    forEach { (k, t) ->

    }
    for ((index, k) in this.keys.withIndex()) {
        val value = this.values.getValue(index)
        if (action(k, value)) {
            break
        }
    }
}

public inline fun <K, T> Map<K, T>?.where(crossinline action: K.(value: T?) -> Boolean): MutableList<T> {
    if (this == null) {
        return mutableListOf()
    }
    val list = mutableListOf<T>()
    this.forEach { (k, t) ->
        if (action(k, t)) {
            list.add(t)
        }
    }
    return list
}

fun <K, T> Map<K, T>?.like(key: K): String {
    return optString(key, "%%") {
        return@optString "%$this%"
    }
}